#include "SphereSceneResources.h"

#include <cassert>
#include <d3d11.h>
#include <DirectXMath.h>

#include "main/Globals.h"

#include "managers/ShaderResourcesManager.h"

#include "scenes/sphereScene/SphereSceneSettings.h"

#include "utils/MathUtils.h"

using namespace DirectX;

namespace sphereScene {
	Resources::Resources(const Settings& settings)
		: mQuadTextureSRV(nullptr)
		, mEntitiesPosW(nullptr)
		, mEntitiesPosWSRV(nullptr)
		, mEntitiesPosWUAV(nullptr)
		, mEntitiesVelW(nullptr)
		, mEntitiesVelWUAV(nullptr)
	{
		// Texture used for entities
		assert(Globals::gResources);
		mQuadTextureSRV = Globals::gResources->addTextureFromFileSRV(L"particle", L"tests/particle.dds");
		assert(mQuadTextureSRV);

		// Entities buffer
		buildEntitiesBuffers(settings.mNumEntities);

		//
		// Vertex shader resources
		//

		// Entities position buffer SRV
		{
			const uint32_t numEntities = settings.mNumEntities;
			D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
			srvDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
			srvDesc.Buffer.FirstElement = 0;
			srvDesc.Buffer.NumElements = numEntities;
			mEntitiesPosWSRV = Globals::gResources->addResourceSRV(L"entitiesPosWSRV", mEntitiesPosW, srvDesc);
			assert(mEntitiesPosWSRV);
		}


		//
		// Compute shader resources
		// 

		// Entities position buffer UAV
		{
			const uint32_t numEntities = settings.mNumEntities;
			D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
			uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
			uavDesc.Buffer.FirstElement = 0;
			uavDesc.Buffer.Flags = 0;
			uavDesc.Buffer.NumElements = numEntities;
			mEntitiesPosWUAV = Globals::gResources->addResourceUAV(L"entitiesPosWUAV", mEntitiesPosW, uavDesc);
			assert(mEntitiesPosWUAV);
		}

		// Entities velocity buffer UAV
		{
			const uint32_t numEntities = settings.mNumEntities;
			D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
			uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
			uavDesc.Buffer.FirstElement = 0;
			uavDesc.Buffer.Flags = 0;
			uavDesc.Buffer.NumElements = numEntities;
			mEntitiesVelWUAV = Globals::gResources->addResourceUAV(L"entitiesVelWUAV", mEntitiesVelW, uavDesc);
			assert(mEntitiesVelWUAV);
		}
	}

	void
	Resources::buildEntitiesBuffers(const uint32_t numEntities) {
		assert(mEntitiesPosW == nullptr);
		assert(mEntitiesVelW == nullptr);
		
		//
		// Create entities position buffer
		//
		{
			// Create data to fill buffer.
			D3D11_SUBRESOURCE_DATA initData;
			std::vector<XMFLOAT4> entitiesPosW;
			entitiesPosW.reserve(numEntities);
			const float sphereRadius = 320.0f;
			const float innerRadius = 310.0f;

			for (uint32_t i = 0; i < numEntities; ++i) {
				const float posX = randomFloat(-sphereRadius, sphereRadius);
				const float posY = randomFloat(-sphereRadius, sphereRadius);
				const float posZ = randomFloat(-sphereRadius, sphereRadius);
				XMFLOAT3 pos(posX, posY, posZ);
				XMVECTOR posVec = XMLoadFloat3(&pos);
				posVec = XMVector3Normalize(posVec);
				float randomRadius = 0.0f;
				while (-innerRadius < randomRadius && randomRadius < innerRadius) {
					randomRadius = randomFloat(-sphereRadius, sphereRadius);
				}

				setLength(posVec, randomRadius);
				XMStoreFloat3(&pos, posVec);

				const XMFLOAT4 posW(pos.x, pos.y, pos.z, 1.0f);
				entitiesPosW.push_back(posW);
			}

			initData.pSysMem = &entitiesPosW[0];

			// Create buffer.
			D3D11_BUFFER_DESC bufferDesc;
			ZeroMemory(&bufferDesc, sizeof(D3D11_BUFFER_DESC));
			bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
			bufferDesc.ByteWidth = sizeof(XMFLOAT4) * numEntities;
			bufferDesc.CPUAccessFlags = 0;
			bufferDesc.MiscFlags = 0;
			bufferDesc.StructureByteStride = 0;
			bufferDesc.Usage = D3D11_USAGE_DEFAULT;
			mEntitiesPosW = Globals::gResources->addBuffer(L"entitiesPosW", bufferDesc, &initData);
			assert(mEntitiesPosW);
		}

		//
		// Create entities velocity buffer
		//
		{
			// Create data to fill buffer.
			D3D11_SUBRESOURCE_DATA initData;
			std::vector<XMFLOAT4> entitiesVelW;
			entitiesVelW.reserve(numEntities);
			const float vel = 0.01f;

			for (uint32_t i = 0; i < numEntities; ++i) {
				const float velX = randomFloat(-vel, vel);
				const float velY = randomFloat(-vel, vel);
				const float velZ = randomFloat(-vel, vel);
				const XMFLOAT4 velW(velX, velY, velZ, 1.0f);
				entitiesVelW.push_back(velW);
			}

			initData.pSysMem = &entitiesVelW[0];

			// Create buffer.
			D3D11_BUFFER_DESC bufferDesc;
			ZeroMemory(&bufferDesc, sizeof(D3D11_BUFFER_DESC));
			bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
			bufferDesc.ByteWidth = sizeof(XMFLOAT4) * numEntities;
			bufferDesc.CPUAccessFlags = 0;
			bufferDesc.MiscFlags = 0;
			bufferDesc.StructureByteStride = 0;
			bufferDesc.Usage = D3D11_USAGE_DEFAULT;
			mEntitiesVelW = Globals::gResources->addBuffer(L"entitiesVelW", bufferDesc, &initData);
			assert(mEntitiesVelW);
		}
	}
}